<!DOCTYPE html>
<meta charset="utf-8">

<head>
    <link href='https://fonts.googleapis.com/css?family=Play' rel='stylesheet' type='text/css'>
    <style>
        body {
            margin: auto;
            font-family: 'Play', sans-serif;
            font-size: 100%;
            cursor: default;
            position: relative;
            top: 200px;
            left: 26%;
        }

        #dashboard {
            width: 100%;
        }

        text {
            font-family: 'Play', sans-serif;
            pointer-events: none;
        }

        rect {
            fill: #428bca;
        }

        .zoomButton {
            font-size: 33px;
            fill: white;
        }

        .zoomButton.plus rect {
            fill: #5cb85c;
        }

        .zoomButton.minus rect {
            fill: #d9534f;
        }

        .active {
            opacity: 1;
            transition: opacity 1000ms;
        }

        .inactive {
            opacity: .1;
            transition: opacity 1000ms;
        }
    </style>


    <script src="https://d3js.org/d3.v4.min.js"></script>
    <!-- <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> -->


    <body>
        <div id="dashboard"></div>
        <script>
            //画布大小
            var margin = { top: 0, left: 50, bottom: 50, right: 75 },
                width = 960 - margin.left - margin.right,
                height = 125 - margin.top - margin.bottom;

            //图形大小
            var svg = d3.select("body").append("svg")
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


            //随机正态分布1000个数字
            var data = d3.range(1000).map(d3.randomNormal(80, 10));

            //排序
            data.sort(function (a, b) { return a - b; });

            //取最大最小值范围
            var x = d3.scaleLinear()
                .domain(d3.extent(data))
                .range([0, width]);

            //随机取纵坐标
            var ticks = { req: { min: 5, max: 40 }, got: [] };

            (function () {
                var t = 0;
                //随机取最大值到最小值之间的值域数量 取中间数
                var xt = d3.range(ticks.req.min, ticks.req.max + 1, 1)
                xt.forEach(function (d) {
                    if (x.ticks(d).length > t) {
                        t = x.ticks(d).length;
                        ticks.got.push(x.ticks(d).length)
                    }
                });
            })();

            ticks.def = ticks.got[Math.round(ticks.got.length / 2) - 1];
            ticks.cur = ticks.def;
            console.log(ticks)

            //取每段值域内的数值
            var bins = binData(data, x, ticks.def);
            console.log(bins)

            //定义Y轴
            var y = d3.scaleLinear()
                .range([height, 0])
                .domain([0, d3.max(bins.map(function (d) { return d.count }))]);

            //xy轴容器
            var xAxis = d3.axisBottom(x),
                yAxis = d3.axisLeft(y);

            //直方图容器
            svg.selectAll(".bar")
                .data(bins)
                .enter()
                .append("rect")
                .attr("class", "bar")
                .attr("width", x(bins[0].max) - x(bins[0].min) - 2)
                .attr("height", function (d) { return height - y(d.count) })
                .attr("x", function (d) { return x(d.min) })
                .attr("y", function (d) { return y(d.count) });

            //x轴显示
            svg.append("g")
                .attr("class", "axis x")
                .attr("transform", "translate(0," + height + ")")
                .call(xAxis)

            //y轴显示
            svg.append("g")
                .attr("class", "axis y")
                .call(yAxis);


            //添加+按钮 绑定zoom 点击事件， 在条件满足时为active 否则为inactive
            var plusButton = svg.append("g")
                .attr("class", "zoomButton plus")
                .attr("transform", "translate(" + width + ",0)")
                .classed("active", ticks.got.indexOf(ticks.cur) < (ticks.got.length - 1))
                .classed("inactive", ticks.got.indexOf(ticks.cur) == (ticks.got.length - 1))
                .on("click", zoom)

            plusButton.append("rect")
                .attr("width", 20)
                .attr("height", 20)

            plusButton.append("text")
                .attr("x", 10)
                .attr("y", 20)
                .text("+")
                .attr("text-anchor", "middle")

            var minusButton = svg.append("g")
                .attr("class", "zoomButton minus")
                .attr("transform", "translate(" + (width+20) + "," + 0 + ")")
                .classed("active", ticks.got.indexOf(ticks.cur) > 0)
                .classed("inactive", ticks.got.indexOf(ticks.cur) == 0)
                .on("click", zoom)

            minusButton.append("rect")
                .attr("width", 20)
                .attr("height", 20)

            minusButton.append("text")
                .attr("x", 10)
                .attr("y", 20)
                .attr("dy", -2)
                .text("-")
                .attr("text-anchor", "middle");

            var duration = 1000;

            function binData(dat, s, t) {
                var b = [];

                s.ticks(t).forEach(function (d, i) {
                    if (i < x.ticks(t).length - 1) {
                        b.push({ bin: i, min: d, max: x.ticks(t)[i + 1] })
                    }
                });

                b.forEach(function (d) {
                    d.count = dat.filter(function (e) { return e >= d.min && e < d.max }).length;
                })
                return b;
            }

            function zoom() {

                if (d3.select(this).classed("inactive")) { return 0; }

                if (d3.select(this).classed("plus")) {
                    ticks.cur = ticks.got[ticks.got.indexOf(ticks.cur) + 1]
                } else {
                    ticks.cur = ticks.got[ticks.got.indexOf(ticks.cur) - 1]
                }

                minusButton.classed("active", ticks.got.indexOf(ticks.cur) > 0)
                    .classed("inactive", ticks.got.indexOf(ticks.cur) == 0)

                plusButton.classed("active", ticks.got.indexOf(ticks.cur) < (ticks.got.length - 1))
                    .classed("inactive", ticks.got.indexOf(ticks.cur) == (ticks.got.length - 1))

                //改变横坐标值域
                svg.select(".axis.x")
                    .transition()
                    .duration(duration)//延时1000
                    .call(xAxis.ticks(ticks.cur))

                //改变d3直方图数据
                var newBins = binData(data, x, ticks.cur);

                //改变纵坐标值域
                y.domain([0, d3.max(newBins.map(function (d) { return d.count }))]);
                svg.select(".axis.y")
                    .transition()
                    .duration(duration)
                    .call(yAxis);

                //移除原有的直方图
                svg.selectAll(".bar")
                    .transition()
                    .duration(duration)
                    .attr("height", function (d) { return height - y(0) })
                    .attr("y", function (d) { return y(0) })
                    .transition()
                    .remove();

                //创建现有数据的直方图
                svg.selectAll(null)
                    .data(newBins)
                    .enter()
                    .append("rect")
                    .attr("class", "bar")
                    .attr("width", x(newBins[0].max) - x(newBins[0].min) - 2)
                    .attr("height", function (d) { return height - y(0) })
                    .attr("y", function (d) { return y(0) })
                    .attr("x", function (d) { return x(d.min) })
                    .transition()
                    .duration(duration)
                    .attr("height", function (d) { return height - y(d.count) })
                    .attr("y", function (d) { return y(d.count) });

            }





        </script>